package com.xbh.management.utils;

import com.alibaba.fastjson.JSON;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jwk.RSAKey;
import com.xbh.management.entity.PayloadDTO;
import com.xbh.management.exception.JwtSignatureVerifyException;
import com.xbh.management.model.ManUser;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.ParseException;

/**
 * @program: management-center
 * @description:
 * @author: 许宝华
 * @create: 2021-09-08 15:32
 */
@Component
public class JwtUtilManagement {

//    @Value("${my.jwt.signature.algorithm.secret}")注意因为JwtUtil没有注册到容器里边，所以取不到值
    private static String signatureAlgorithmSecret="abcdefghijklmnopqrstuvwxyz123456789";

    private static Long tokenExpirationTime=36000L;

    /**
     * 生成token -- 采用【对称加密】算法HS256验证签名
     * <p>
     * 注:此方法生成的jwt的Header部分为默认的
     * {
     * "alg": "HS256",
     * "typ": "JWT"
     * }
     *
     * @param payloadJsonString 有效负载JSON字符串
     * @param secret            对称加密/解密密钥
     *                          注意:secret.getBytes().length 必须 >=32
     */
    public static String generateToken(String payloadJsonString, String secret) throws JOSEException {

        // 创建Header(设置以JWSAlgorithm.HS256算法进行签名认证)
        JWSHeader jwsHeader = new JWSHeader(JWSAlgorithm.HS256);

        // 建立Payload
        Payload payload = new Payload(payloadJsonString);

        /// Signature相关
        // 根据Header以及Payload创建JSON Web Signature (JWS)对象
        JWSObject jwsObject = new JWSObject(jwsHeader, payload);
        // 使用给的对称加密密钥，创建一个加密器
        JWSSigner jwsSigner = new MACSigner(secret);
        // 将该签名器 与 JSON Web Signature (JWS)对象进行关联
        // 即: 指定JWS使用该签名器进行签名
        jwsObject.sign(jwsSigner);

        // 使用JWS生成JWT(即:使用JWS生成token)
        return jwsObject.serialize();
    }

    /**
     * 校验token是否被篡改，并返回有效负载JSON字符串 -- 采用【对称加密】算法HS256验证签名
     *
     * @param secret 对称加密/解密密钥
     *               注意:secret.getBytes().length 必须 >=32
     * @return 有效负载JSON字符串
     * @throws JOSEException,ParseException,JwtSignatureVerifyException 异常信息
     * @date 2019/7/21 14:08
     */
    public static String verifySignature(String token, String secret) throws JOSEException, ParseException, JwtSignatureVerifyException {
        // 解析token，将token转换为JWSObject对象
        JWSObject jwsObject = JWSObject.parse(token);

        // 创建一个JSON Web Signature (JWS) verifier.用于校验签名(即:校验token是否被篡改)
        JWSVerifier jwsVerifier = new MACVerifier(secret);
        // 如果校验到token被篡改(即:签名认证失败)，那么抛出异常
        if (!jwsObject.verify(jwsVerifier)) {
            throw new JwtSignatureVerifyException("Signature verification result is fail!");
        }

        // 获取有效负载
        Payload payload = jwsObject.getPayload();

        // 返回 有效负载JSON字符串
        return payload.toString();
    }


    /**
     * 生成token -- 采用【非对称加密】算法RS256验证签名
     *
     * @param payloadJsonString 有效负载JSON字符串
     * @param rsaKey            非对称加密密钥对
     *                          提示:RSAKey示例，可以这么获得
     *                          RSAKeyGenerator rsaKeyGenerator = new RSAKeyGenerator(1024 * 3);
     *                          RSAKey rsaKey = rsaKeyGenerator.generate();
     * @return token
     * @throws JOSEException 异常信息
     */
    public static String generateTokenByAsymmetric(String payloadJsonString, RSAKey rsaKey) throws JOSEException {

        // Header
        JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.RS256)
                .keyID(rsaKey.getKeyID())
                .build();
        // Payload
        Payload payload = new Payload(payloadJsonString);

        /// Signature相关
        // 根据Header以及Payload创建JSON Web Signature (JWS)对象
        JWSObject jwsObject = new JWSObject(jwsHeader, payload);
        // 使用给的对称加密密钥，创建一个加密器
        JWSSigner signer = new RSASSASigner(rsaKey);
        // 将该签名器 与 JSON Web Signature (JWS)对象进行关联
        // 即: 指定JWS使用该签名器进行签名
        jwsObject.sign(signer);

        // 使用JWS生成JWT(即:使用JWS生成token)
        return jwsObject.serialize();
    }


    /**
     * 校验token是否被篡改，并返回有效负载JSON字符串 -- 采用【非对称加密】算法RS256验证签名
     *
     * @param rsaKey 非对称加密密钥对
     */
    public static String verifySignatureByAsymmetric(String token, RSAKey rsaKey) throws JOSEException, ParseException, JwtSignatureVerifyException {
        // 根据token获得JSON Web Signature (JWS)对象
        JWSObject jwsObject = JWSObject.parse(token);
        // 获取到公钥
        RSAKey publicRsaKey = rsaKey.toPublicJWK();
        // 根据公钥 获取 Signature验证器
        JWSVerifier jwsVerifier = new RSASSAVerifier(publicRsaKey);
        // 如果校验到token被篡改(即:签名认证失败)，那么抛出异常
        if (!jwsObject.verify(jwsVerifier)) {
            throw new JwtSignatureVerifyException("Signature verification result is fail!");
        }
        // 获取有效负载
        Payload payload = jwsObject.getPayload();
        // 返回 有效负载JSON字符串
        return payload.toString();

    }

    /**
     * 生成token
     * @param name
     * @return
     */
    public static PayloadDTO getPayload(ManUser user) {
        return PayloadDTO.builder()
                // 放置过期时长
                .exp(tokenExpirationTime)
                // 放置生效时间
                .nbf(System.currentTimeMillis())
                // 放置用户信息
                .name(user.getUsername())
                .userId(user.getUserId())
                .birthday(user.getBirthday())
                .description(user.getDescription())
                .isAdmin(true)
                .build();
    }
    /**
     * 获取请求头中的Authorization参数
     *
     * @param key
     * @return
     */
    private static Object getParams(String key) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String header = request.getHeader(key);
        return header;
    }


    /**
     * 获取token解析后的json有效字符串，返回包装好的PayloadDTO
     * @return
     * @throws ParseException
     * @throws JwtSignatureVerifyException
     * @throws JOSEException
     */
    public static PayloadDTO getJwtPayload() throws ParseException, JwtSignatureVerifyException, JOSEException {

        String token = (String) getParams("Authorization");
        String payloadJsonString;
        JWSObject jwsObject = JWSObject.parse(token);
        payloadJsonString = JwtUtilManagement.verifySignature(token, signatureAlgorithmSecret);
        PayloadDTO payloadDTO = JSON.parseObject(payloadJsonString, PayloadDTO.class);
        return payloadDTO;
    }


    /**
     * 获取解析后的token中的信息，username
     * @return
     * @throws ParseException
     * @throws JwtSignatureVerifyException
     * @throws JOSEException
     */
    public static String getUsername() throws ParseException, JwtSignatureVerifyException, JOSEException {
        String username = getJwtPayload().getName();
        return username;
    }

    /**
     * 获取解析后的token中的信息，UserId
     * @return
     * @throws ParseException
     * @throws JwtSignatureVerifyException
     * @throws JOSEException
     */
    public static Long getUserId() throws ParseException, JwtSignatureVerifyException, JOSEException {
        Long userId = getJwtPayload().getUserId();
        return userId;
    }
}
